/*
Copyright SecureKey Technologies Inc. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package resource

import (
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/providers/context"
	"github.com/golang/protobuf/proto"
	"github.com/pkg/errors"

	"git.cloud.inspur.com/ichain/ichain-protos-go/common"
	"git.cloud.inspur.com/ichain/ichain-sdk-go/thirdparty/ichain/common/crypto"
	"git.cloud.inspur.com/ichain/ichain-sdk-go/thirdparty/ichain/common/util"
)

// CreateConfigSignature 创建配置签名
func CreateConfigSignature(ctx context.Client, config []byte) (*common.ConfigSignature, error) {
	cfd, e := GetConfigSignatureData(ctx, config)
	if e != nil {
		return nil, e
	}

	signingMgr := ctx.SigningManager()
	signature, err := signingMgr.Sign(cfd.SigningBytes, ctx.PrivateKey())
	if err != nil {
		return nil, errors.WithMessage(err, "signing of chain config failed")
	}

	configSignature := common.ConfigSignature{
		SignatureHeader: cfd.SignatureHeaderBytes,
		Signature:       signature,
	}
	return &configSignature, nil
}

type ConfigSignatureData struct {
	SignatureHeader      common.SignatureHeader
	SignatureHeaderBytes []byte
	SigningBytes         []byte
}

type identitySerializer interface {
	Serialize() ([]byte, error)
}

// GetConfigSignatureData 生成配置签名
func GetConfigSignatureData(creator identitySerializer, config []byte) (signatureHeaderData ConfigSignatureData, e error) {
	creatorBytes, err := creator.Serialize()
	if err != nil {
		e = errors.WithMessage(err, "failed to get user context's identity")
		return
	}

	nonce, err := crypto.GetRandomNonce()
	if err != nil {
		e = errors.WithMessage(err, "nonce creation failed")
		return
	}

	signatureHeaderData = ConfigSignatureData{}
	signatureHeaderData.SignatureHeader = common.SignatureHeader{
		Creator: creatorBytes,
		Nonce:   nonce,
	}

	signatureHeaderData.SignatureHeaderBytes, err = proto.Marshal(&signatureHeaderData.SignatureHeader)
	if err != nil {
		e = errors.Wrap(err, "marshal signatureHeader failed")
		return
	}

	// 拼接签名头字节与配置
	signatureHeaderData.SigningBytes = util.ConcatenateBytes(signatureHeaderData.SignatureHeaderBytes, config)

	return
}

// GetLastConfigFromBlock 在指定区块中获取上一次的配置区块序号
func GetLastConfigFromBlock(block *common.Block) (*common.LastConfig, error) {
	if block.Metadata == nil {
		return nil, errors.New("block metadata is nil")
	}
	metadata := &common.Metadata{}
	err := proto.Unmarshal(block.Metadata.Metadata[common.BlockMetadataIndex_LAST_CONFIG], metadata)
	if err != nil {
		return nil, errors.Wrap(err, "unmarshal block metadata failed")
	}

	lastConfig := &common.LastConfig{}
	err = proto.Unmarshal(metadata.Value, lastConfig)
	if err != nil {
		return nil, errors.Wrap(err, "unmarshal last config from metadata failed")
	}

	return lastConfig, err
}
